// $Id: kinetics.c 503 2014-05-07 18:33:02Z alan.watson.f $

// This file is part of the control system for the Oaxaca instrument.

////////////////////////////////////////////////////////////////////////////////

#define _POSIX_C_SOURCE 200112L

#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <sys/time.h>

#include "oaxaca-config.h"
#include "oaxaca.h"
#include "kinetics.h"
#include "array.h"
#include "mprintf.h"
#include "spots.h"
#include "wfits.h"
#include "minmax.h"

static unsigned long nframe = 0;
static unsigned long nwindow;
static array_short_t **z = NULL;

void
oaxaca_kinetics_clear(int index)
{
}

void
oaxaca_kinetics_frame_call_back(int index, array_short_t *frame)
{
  volatile oaxaca_detector_t *detector = &oaxaca_shm->detector[index];

  int ns = detector->ns;
  int searchradius = detector->searchradius;

  if (detector->frame == 0) {
   if (z != NULL) {
     oaxaca_debug("kinetics: freeing storage.");
     for (int iframe = 0; iframe < nframe; ++iframe)
       free_array_short(z[iframe]);
     free(z);
   }
   oaxaca_debug("kinetics: allocating storage.");
   nframe = detector->framesperintegration;
   nwindow = 2 * searchradius + 1;
   z = malloc(nframe * sizeof(array_short_t *));
   if (z == NULL)
     oaxaca_error("kinetics: malloc() returned NULL.");
   for (unsigned long iframe = 0; iframe < nframe; ++iframe)
     z[iframe] = alloc_array_short((ns / 2) * nwindow, nwindow);
  }
  
  unsigned long iframe = detector->frame % detector->framesperintegration;
  //oaxaca_info("kinetics: frame %lu.", iframe);
  
  double r[OAXACA_NS_MAX];
  for (int i = 0; i < ns; ++i)
    r[i] = detector->r[i];

  for (int ispot = 0; ispot < ns / 2; ++ispot) {
    //oaxaca_info("kinetics: frame %lu spot %i", iframe, ispot);
    int rx = r[ispot * 2 + 0];
    int ry = r[ispot * 2 + 1];
    int sx = imin(imax(rx - nwindow / 2, 0), frame->nx - nwindow);
    int sy = imin(imax(ry - nwindow / 2, 0), frame->ny - nwindow);
    //oaxaca_info("kinetics: nwindow = %d sx = %d sy = %d", nwindow, sx, sy);
    //for (int iy = 0; iy < frame->ny; ++iy)
    //  for (int ix = 0; ix < frame->nx; ++ix)
    //    frame->z[iy][ix] = 0;
    //frame->z[sy + nwindow / 2][sx + nwindow / 2] = 1000;
    for (int iy = 0; iy < nwindow; ++iy)
      for (int ix = 0; ix < nwindow; ++ix)
        z[iframe]->z[nwindow * ispot + iy][ix] = frame->z[sy + iy][sx + ix];
  }

}

void
oaxaca_kinetics_integration_call_back(int index, array_short_t *frame, const char *basename)
{
  volatile oaxaca_detector_t *detector = &oaxaca_shm->detector[index];

  int ns = detector->ns;
  char *fitsfilename = mprintf("%sk.fits", basename);

  oaxaca_info("kinetics: writing FITS file \"%s\".", fitsfilename);

  FILE *fp = fopen(fitsfilename, "wb");
  if (fp == NULL)
    oaxaca_error("unable to open FITS file.");

  oaxaca_wfits_write_boolean(fp, "SIMPLE", 1);
  oaxaca_wfits_write_long   (fp, "BITPIX", 16);
  oaxaca_wfits_write_long   (fp, "NAXIS" , 3);
  oaxaca_wfits_write_long   (fp, "NAXIS1", (long) z[0]->nx);
  oaxaca_wfits_write_long   (fp, "NAXIS2", (long) z[0]->ny);
  oaxaca_wfits_write_long   (fp, "NAXIS3", (long) nframe);
  
  oaxaca_wfits_write_date(fp, "DATE");
  oaxaca_wfits_write_string(fp, "ORIGNAME", fitsfilename);
  oaxaca_wfits_write_double(fp, "PXFR", detector->pixelfrequency, "%.2e");
  oaxaca_wfits_write_long(fp, "WNSX", detector->window.sx);
  oaxaca_wfits_write_long(fp, "WNSY", detector->window.sy);
  oaxaca_wfits_write_long(fp, "WNNX", detector->window.nx);
  oaxaca_wfits_write_long(fp, "WNNY", detector->window.ny);
  oaxaca_wfits_write_double(fp, "FRFR", detector->framefrequency, "%.2f");
  oaxaca_wfits_write_double(fp, "ACFRFR", detector->actualframefrequency, "%.2f");
  oaxaca_wfits_write_double(fp, "FRTM", detector->frametime, "%.6f");
  oaxaca_wfits_write_double(fp, "INTM", detector->integrationtime, "%.6f");
  oaxaca_wfits_write_double(fp, "FRPERIN", detector->framesperintegration, "%.0f");

  double r[OAXACA_NS_MAX];
  for (int i = 0; i < ns; ++i)
    r[i] = detector->r[i];

  for (int ispot = 0; ispot < ns / 2; ++ispot) {
    int rx = r[ispot * 2 + 0];
    int ry = r[ispot * 2 + 1];
    int sx = imin(imax(rx - nwindow / 2, 0), frame->nx - nwindow);
    int sy = imin(imax(ry - nwindow / 2, 0), frame->ny - nwindow);
    char *keyword;
    keyword = mprintf("RX%d", ispot);
    oaxaca_wfits_write_long(fp, keyword, rx);
    free(keyword);
    keyword = mprintf("RY%d", ispot);
    oaxaca_wfits_write_long(fp, keyword, ry);
    free(keyword);
    keyword = mprintf("SX%d", ispot);
    oaxaca_wfits_write_long(fp, keyword, sx);
    free(keyword);
    keyword = mprintf("SY%d", ispot);
    oaxaca_wfits_write_long(fp, keyword, sy);
    free(keyword);
  }
  
  fprintf(fp, "%-80.80s", "END");
  
  {
    size_t n = ftell(fp) % 2880;
    if (n > 0) {
      char fill[2880 - n];
      memset(fill, ' ', sizeof(fill));
      fwrite(fill, sizeof(fill), 1, fp);
    }
  }
  
  for (unsigned long iframe = 0; iframe < nframe; ++iframe) {
    array_short_t *data = z[iframe];
    for (size_t iy = 0; iy < data->ny; ++iy) {
      unsigned short buf[data->nx];
      for (size_t ix = 0; ix < data->nx; ++ix) {
        unsigned short us = * (unsigned short *) &data->z[iy][ix];
        buf[ix] = (us << 8) | (us >> 8);
      }
      fwrite(buf, sizeof(buf), 1, fp);
    }
  }

  {
    size_t n = ftell(fp) % 2880;
    if (n > 0) {
      char fill[2880 - n];
      memset(fill, 0, sizeof(fill));
      fwrite(fill, sizeof(fill), 1, fp);
    }
  }
  
  if (fclose(fp) != 0)
    oaxaca_error("while writing FITS file.");

  oaxaca_info("kinetics: finished writing FITS file.");

}
